﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Atha.Contracts;
using Atha.Extensions.Strings;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;

namespace Atha
{
	public class FileSystemTestRunContext : ITestRunContext<AggregateTestResult>
	{
		private readonly IUnityContainer _unityContainer;

		public FileSystemTestRunContext(string inputPath)
		{
			if (string.IsNullOrWhiteSpace(inputPath)) throw new ArgumentNullException("inputPath");

			this._unityContainer = ServiceLocator.Current.GetInstance<IUnityContainer>();

			if (this._unityContainer == null) throw new Exception("An instance of the Unity container was not registered in the ServiceLocator.");

			this.InputPath = inputPath;

			this.IsInputPathDirectory = Directory.Exists(inputPath);

			this.IsInputPathFile = this.IsInputPathDirectory ? false : File.Exists(inputPath);

			bool hasExtension = Path.HasExtension(inputPath);

			if (!this.IsInputPathDirectory && !this.IsInputPathFile)
			{
				if (hasExtension) throw new FileNotFoundException("Input file was not found", inputPath);
				else throw new DirectoryNotFoundException("Input directory was not found");
			}

			if (this.IsInputPathFile && !hasExtension)
			{
				throw new ArgumentException("Input file exists, but has no extension. The file extension directs the language chosen by AthaX to run the test script.", "inputPath");
			}
		}

		public string InputPath { get; private set; }

		public bool IsInputPathDirectory { get; private set; }

		public bool IsInputPathFile { get; private set; }

		public AggregateTestResult Execute()
		{
			var testResults = new List<AggregateTestResult>();

			try
			{
				if (this.IsInputPathFile)
				{
					testResults.Add(ExecuteTestScript(this.InputPath));
				}
				else if (this.IsInputPathDirectory)
				{
					var files = Directory.GetFiles(this.InputPath, "*", SearchOption.AllDirectories);

					foreach (var file in files)
					{
						testResults.Add(ExecuteTestScript(file));
					}
				}
			}
			catch (Exception ex)
			{
				testResults.Add(new AggregateTestResult("Exception", new[] { new ExceptionTestResult(ex) }));
			}

			return new AggregateTestResult(this.InputPath, testResults);
		}

		private AggregateTestResult ExecuteTestScript(string inputPath)
		{
			if (!Path.HasExtension(inputPath))
				return new AggregateTestResult(
					"Missing file extension".FormatWith(inputPath),
					new[] { new ErrorTestResult("Input file is missing a file extension \"{0}\". The file extension directs the language chosen by AthaX.".FormatWith(inputPath)) });

			var inputFileExtension = Path.GetExtension(inputPath);

			string testRunnerName = null;

			// TODO: move test runner extension mappings to Configuration
			switch (inputFileExtension.ToUpperInvariant())
			{
				case ".PY":
					testRunnerName = "IronPython";
					break;
				case ".RB":
					testRunnerName = "IronRuby";
					break;
				case ".PS1":
					testRunnerName = "PowerShell";
					break;
				case ".CSHTML":
				case ".VBHTML":
					testRunnerName = "Razor";
					break;
				default:
					return new AggregateTestResult(
						"Unrecognized file extension \"{0}\"".FormatWith(inputFileExtension),
						new[] { new ErrorTestResult("Unrecognized input file extension \"{0}\". The file extension directs the language chosen by AthaX.".FormatWith(inputFileExtension)) });
			}

			var testScriptContent = File.ReadAllText(inputPath);

			var testRunContext = this._unityContainer.Resolve<ITestRunContext<AggregateTestResult>>(
				testRunnerName,
				new ParameterOverride("name", inputPath),
				new ParameterOverride("script", testScriptContent));

			Debug.Assert(testRunContext != null);

			var testResults = testRunContext.Execute();

			return testResults;
		}
	}
}
